#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>

extern "C" JNIEXPORT jstring JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}


extern "C"
JNIEXPORT void JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_testObject(JNIEnv *env, jobject thiz) {
    //创建Person对象
    const char *person_class_str = "com/xia/ndk09_as_code/Person";
    jclass person_class = env->FindClass(person_class_str);
    jobject person = env->AllocObject(person_class);
    //创建签名
    const char *person_sign = "(Lcom/xia/ndk09_as_code/Student;)V";
    jmethodID setStudent = env->GetMethodID(person_class, "setStudent", person_sign);
    //创建Student对象
    const char *student_class_str = "com/xia/ndk09_as_code/Student";
    jclass student_class = env->FindClass(student_class_str);
    jobject student = env->AllocObject(student_class);
    const char *student_sign = "(Ljava/lang/String;)V";
    //给Student赋值
    jmethodID setName = env->GetMethodID(student_class, "setName", student_sign);
    jstring value = env->NewStringUTF("张三丰");
    env->CallVoidMethod(student, setName, value);
    const char *student_sign2 = "(I)V";
    jmethodID setAge = env->GetMethodID(student_class, "setAge", student_sign2);
    env->CallVoidMethod(student, setAge, 105);
    //调用Person里的方法
    env->CallVoidMethod(person, setStudent, student);

    // xia chen hui 2020/5/31 20:36 局部引用，在函数里，如果在栈区，函数结束后会被自动回收，但建议自己回收
    //回收 jObject  jClass
    env->DeleteLocalRef(student);
    env->DeleteLocalRef(student_class);
    env->DeleteLocalRef(person);
    env->DeleteLocalRef(person_class);

}

// TODO xia chen hui 2020/5/31 20:35 引用类型 + Java 构造方法的实例化

jclass dogClass;
extern "C"
JNIEXPORT void JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_testDog(JNIEnv *env, jobject thiz) {
    if (dogClass == NULL) {
        //局部引用方式 ,第一次会正常进来，隐士释放之后，第二次就不会走进来了。
        const char *dog_class_str = "com/xia/ndk09_as_code/Dog";
        // dogClass = env->FindClass(dog_class_str);


        //解决局部引用带来的问题，使用全局引用(只能手动提升为全局)
        jclass temp = env->FindClass(dog_class_str);
        dogClass = static_cast<jclass>(env->NewGlobalRef(temp));

        __android_log_print(ANDROID_LOG_DEBUG, "Forrest", ",不手动置空的话，只会走一次");
    }

    //Java 构造方法实例化
    const char *dog_sing = "()V"; //构造方法签名
    const char *method = "<init>"; //构造方法标识
    jmethodID dogMethodId = env->GetMethodID(dogClass, method, dog_sing);
    env->NewObject(dogClass, dogMethodId); // 第二次因为dogClass是悬空指针，所以会崩溃
    // 局部引用会隐士释放dogClass，但是dogClass不为NUll，变成悬空指针 ，手动释放也是一样的效果，需要自己置空
}

extern "C"
JNIEXPORT void JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_releaseDog(JNIEnv *env, jobject thiz) {
    //手动释放全局引用
    if (dogClass != NULL) {
        __android_log_print(ANDROID_LOG_DEBUG, "Forrest", "全局引用被释放了，上面的引用类型测试不能再点击，否则会报错...");
        env->DeleteGlobalRef(dogClass);
        dogClass = NULL; //C++之后提出的，否则dogClass不会为NULL
    }
}

// TODO xia chen hui 2020/5/31 21:04 动态注册,上面的都是静态注册,下面的是动态注册
/*
typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;*/

void register01(JNIEnv *env, jobject instance, jstring text) {
    const char *result = env->GetStringUTFChars(text, NULL);
    __android_log_print(ANDROID_LOG_DEBUG, "Forrest", "动态注册的函数执行了%s", result);
    env->ReleaseStringUTFChars(text, result);
};
//批量注册
static const JNINativeMethod jniNativeMethod[] = {
        {"registerJava01", "(Ljava/lang/String;)V", (void *) (register01)}
};
JavaVM *mJavaVm;
// xia chen hui 2020/5/31 21:11  System.loadLibrary("native-lib")的时候会调用下面的方法
JNIEXPORT jint  JNICALL JNI_OnLoad(JavaVM *javaVm, void *pVoid) {
    ::mJavaVm = javaVm;

    //通过虚拟机创建全新的env
    JNIEnv *env = nullptr;
    jint result = javaVm->GetEnv(reinterpret_cast<void **>(&env),
                                 JNI_VERSION_1_6);//参数2 是JNI的版本  NDK 1.6
    if (result != JNI_OK) {
        //失败,主动报错
        return -1;
    }
    const char *mainActivityClass_str = "com/xia/ndk09_as_code/MainActivity";
    jclass mainActivityClass = env->FindClass(mainActivityClass_str);
    //参数3 ：要注册几个
    env->RegisterNatives(mainActivityClass, jniNativeMethod,
                         sizeof(jniNativeMethod) / sizeof(JNINativeMethod));

    return JNI_VERSION_1_6;
}

jobject instance; //这个就是MainActivity
// TODO xia chen hui 2020/5/31 21:49 JNI线程
void *customThread(void *pVoid) {
    //调用Android的话，一定需要env，但是env无法跨线程,只有JavaVm才能跨线程.
    JNIEnv *env = nullptr;
    int result = mJavaVm->AttachCurrentThread(&env, 0);//把Native的线程，附加到JVM，获取env
    if (result != 0) {
        // 失败了
        return 0;
    }
    //拿到MainActivity的updateUI ,不能自己创建MainActivity
    /* const char *mainActivityClass_str = "com/xia/ndk09_as_code/MainActivity";
     jclass mainActivityClass = env->FindClass(mainActivityClass_str);*/
    jclass mainActivityClass = env->GetObjectClass(instance);
    const char *sign = "()V";
    jmethodID updateUIID = env->GetMethodID(mainActivityClass, "updateUI", sign);
    env->CallVoidMethod(instance, updateUIID);
    //接触jvm的附加
    mJavaVm->DetachCurrentThread();
    return 0;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_testThread(JNIEnv *env, jobject thiz) {
    //把thiz全局引用，然后传递到线程里去 ，这就是MainActivity对象
    instance = env->NewGlobalRef(thiz);
    pthread_t pthreadID;
    pthread_create(&pthreadID, 0, customThread, instance);
    pthread_join(pthreadID, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_xia_ndk09_1as_1code_MainActivity_unThread(JNIEnv *env, jobject thiz) {
   if(instance!=NULL){
       env->DeleteGlobalRef(instance);
       instance= NULL;
       __android_log_print(ANDROID_LOG_DEBUG,"Forrest","线程释放了...");
   }
}